﻿using System;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using CID.Domain;
using CID.Support.Exceptions;
using CID.Web.Models;
using NailsFramework.IoC;
using NailsFramework.Persistence;
using NailsFramework.UserInterface;
using NailsFramework.UnitOfWork;

namespace CID.Web.Controllers
{
    public abstract class BaseCRUDController<TModelInterface, TModel, TViewModel> : NailsController 
        where TModelInterface : IEntity
        where TModel: class, TModelInterface
        where TViewModel : BaseViewModel<TModelInterface>, new()
    {
        private ConstructorInfo constructorInfo;

        public BaseCRUDController()
        {
            constructorInfo = typeof(TViewModel).GetConstructor(new Type[] { typeof(TModel) });
        }

        [Inject]
        public virtual CurrentUnitOfWork CurrentUnitOfWork { protected get; set; }

        [Inject]
        public virtual IBag<TModel> Entities { protected get; set; }

        protected virtual TModel GetModel(int id)
        {
            return Entities.Single(x => x.Id == id);
        }

        protected virtual TViewModel CreateViewModel()
        {
            var result = new TViewModel();
            PrepareViewModelForView(result);
            return result;
        }

        protected virtual TViewModel GetViewModel(int id)
        {
            var result = (TViewModel)(constructorInfo.Invoke(new object[] { GetModel(id) }));
            PrepareViewModelForView(result);
            return result;
        }

        protected abstract TModel CreateModel(TViewModel viewModel);

        /// <summary>
        /// Configures the ViewModel instance with any additional features that were not set in the
        /// construction of the object but are neede for the correct display in the View
        /// </summary>
        /// <param name="viewModel">ViewModel instance to prepare</param>
        protected virtual void PrepareViewModelForView(TViewModel viewModel)
        {
        }

        public virtual ViewResult Index()
        {
            return View(Entities);
        }

        public virtual ViewResult Details(int id)
        {
            return View(GetViewModel(id));
        }

        public virtual ViewResult Create()
        {
            return View(CreateViewModel());
        } 

        [HttpPost]
        public virtual ActionResult Create(TViewModel viewModel)
        {
            PrepareViewModelForView(viewModel);
            if (!ModelState.IsValid) return View(viewModel);
            try
            {
                var newModel = CreateModel(viewModel);
                viewModel.UpdateModel(newModel);
                Entities.Put(newModel);
                return RedirectToAction("Index");
            }
            catch (DomainException ex)
            {
                CurrentUnitOfWork.Cancel();
                ModelState.AddModelError("", ex.Message);
                return View(viewModel);
            }
        }
 
        public virtual ViewResult Edit(int id)
        {
            return View(GetViewModel(id));
        }

        [HttpPost]
        public virtual ActionResult Edit(int id, TViewModel viewModel)
        {
            PrepareViewModelForView(viewModel);
            if (!ModelState.IsValid) return View(viewModel);
            try
            {
                var editedModel = GetModel(id);
                viewModel.UpdateModel(editedModel);

                return RedirectToAction("Index");
            }
            catch (DomainException ex)
            {
                CurrentUnitOfWork.Cancel();
                ModelState.AddModelError("", ex.Message);
                return View(viewModel);
            }
        }

        //public virtual ViewResult Delete(int id)
        //{
        //    return View(GetViewModel(id));
        //}

        //[HttpPost]
        //public virtual ActionResult Delete(int id, TViewModel viewModel)
        //{
        //    try
        //    {
        //        Entities.Remove(GetModel(id));
 
        //        return RedirectToAction("Index");
        //    }
        //    catch
        //    {
        //        return View(viewModel);
        //    }
        //}
    }
}
